#include <cassert>
#include <stdexcept>

namespace Pdf
{
    template <typename Handler>
    std::streambuf::int_type Scanner<Handler>::scanNext(std::streambuf::int_type c, std::streambuf *s)
    {
        if ( c != std::streambuf::traits_type::eof() )
        {
            switch ( c )
            {
            case '\0':
            case '\t':
            case '\f':
            case ' ':
                static_cast<Handler*>(this)->onEnterWhiteSpace();
                c = scanWhiteSpace( c, s );
                static_cast<Handler*>(this)->onLeaveWhiteSpace();
                break;
            case '\n':
            case '\r':
                static_cast<Handler*>(this)->onEnterEOL();
                c = scanEOL( c, s );
                static_cast<Handler*>(this)->onLeaveEOL();
                break;
            case '%':
                static_cast<Handler*>(this)->onEnterComment();
                c = scanLine( s->snextc(), s );
                static_cast<Handler*>(this)->onLeaveComment();
                break;
            case '(':
                static_cast<Handler*>(this)->onEnterString();
                c = scanLexString( s->snextc(), s );
                static_cast<Handler*>(this)->onLeaveString();
                break;
            case ')':
                throw std::runtime_error("unexpected end of literal string");
            case '/':
                static_cast<Handler*>(this)->onEnterName();
                c = scanName( s->snextc(), s );
                static_cast<Handler*>(this)->onLeaveName();
                break;
            case '<':
                c = scanDictionaryBeg( s->snextc(), s );
                break;
            case '>':
                c = scanDictionaryEnd( s->snextc(), s );
                break;
            case '[':
                static_cast<Handler*>(this)->onEnterArray();
                c = s->snextc();
                break;
            case ']':
                static_cast<Handler*>(this)->onLeaveArray();
                c = s->snextc();
                break;
            case '{':
                static_cast<Handler*>(this)->onEnterNames();
                c = s->snextc();
                break;
            case '}':
                static_cast<Handler*>(this)->onLeaveNames();
                c = s->snextc();
                break;
            default:
                static_cast<Handler*>(this)->onEnterToken();
                c = scanToken( c, s );
                static_cast<Handler*>(this)->onLeaveToken();
            }
        }
        return c;
    }

    template <typename Handler>
    std::streambuf::int_type Scanner<Handler>::scanWhiteSpace(std::streambuf::int_type c, std::streambuf *s)
    {
        while ( c != std::streambuf::traits_type::eof() )
        {
            switch ( c )
            {
            case '\0':
            case '\t':
            case '\f':
            case ' ':
                static_cast<Handler*>(this)->onCharacter( static_cast<char>( c ) );
                break;
            default:
                return c;
            }
            c = s->snextc();
        }
        return c;
    }

    template <typename Handler>
    std::streambuf::int_type Scanner<Handler>::scanEOL(std::streambuf::int_type c, std::streambuf *s)
    {
        switch ( c )
        {
        case '\r':
            static_cast<Handler*>(this)->onCharacter( static_cast<char>( c ) );
            return scanEOL2( s->snextc(), s );
        case '\n':
            static_cast<Handler*>(this)->onCharacter( static_cast<char>( c ) );
            return s->snextc();
        default:
            return c;
        }
    }

    template <typename Handler>
    std::streambuf::int_type Scanner<Handler>::scanEOL2(std::streambuf::int_type c, std::streambuf *s)
    {
        switch ( c )
        {
        case '\n':
            static_cast<Handler*>(this)->onCharacter( static_cast<char>( c ) );
            return s->snextc();
        default:
            return c;
        }
    }

    template <typename Handler>
    std::streambuf::int_type Scanner<Handler>::scanLine(std::streambuf::int_type c, std::streambuf *s)
    {
        while ( c != std::streambuf::traits_type::eof() )
        {
            switch ( c )
            {
            case '\r':
            case '\n':
                return c;
            default:
                static_cast<Handler*>(this)->onCharacter( static_cast<char>( c ) );
            }
            c = s->snextc();
        }
        return c;
    }

    template <typename Handler>
    std::streambuf::int_type Scanner<Handler>::scanLexString(std::streambuf::int_type c, std::streambuf *s)
    {
        int parentheses = 1;

        while ( c != std::streambuf::traits_type::eof() )
        {
            if ( c == '\\' )
                c = scanLexStringEsc( s->snextc(), s );
            else
            {
                switch ( c )
                {
                case '(': ++parentheses; break;
                case ')': --parentheses; break;
                }

                if (parentheses == 0)
                    return s->snextc();

                static_cast<Handler*>(this)->onCharacter( static_cast<char>( c ) );
                c = s->snextc();
            }
        }
        return c;
    }

    template <typename Handler>
    std::streambuf::int_type Scanner<Handler>::scanLexStringEsc(std::streambuf::int_type c, std::streambuf *s)
    {
        switch ( c )
        {
        case '\n':
        case '\r':
            c = scanLexStringEOL( c, s );
            break;
        case 'n':
            static_cast<Handler*>(this)->onCharacter( '\n' );
            c = s->snextc();
            break;
        case 'r':
            static_cast<Handler*>(this)->onCharacter( '\r' );
            c = s->snextc();
            break;
        case 't':
            static_cast<Handler*>(this)->onCharacter( '\t' );
            c = s->snextc();
            break;
        case 'b':
            static_cast<Handler*>(this)->onCharacter( '\b' );
            c = s->snextc();
            break;
        case 'f':
            static_cast<Handler*>(this)->onCharacter( '\f' );
            c = s->snextc();
            break;
        case '(':
        case ')':
        case '\\':
            static_cast<Handler*>(this)->onCharacter( static_cast<char>( c ) );
            c = s->snextc();
            break;
        case '0':
        case '1':
        case '2':
        case '3':
        case '4':
        case '5':
        case '6':
        case '7':
            c = scanLexStringOct( c, s );
            break;
        default:
            throw std::runtime_error("invalid literal string escape sequence");
        }
        return c;
    }

    template <typename Handler>
    std::streambuf::int_type Scanner<Handler>::scanLexStringOct(std::streambuf::int_type c, std::streambuf *s)
    {
        std::streambuf::int_type octChar = 0;

        for (int i = 0; i < 3; ++i)
        {
            switch ( c )
            {
            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
                octChar = octChar * 8 + c - '0';
                break;
            default:
                static_cast<Handler*>(this)->onCharacter( static_cast<char>(octChar) );
                return c;
            }
            c = s->snextc();
        }

        static_cast<Handler*>(this)->onCharacter( static_cast<char>(octChar) );
        return c;
    }

    template <typename Handler>
    std::streambuf::int_type Scanner<Handler>::scanLexStringEOL(std::streambuf::int_type c, std::streambuf *s)
    {
        switch ( c )
        {
        case '\r':
            return scanLexStringEOL2( s->snextc(), s );
        case '\n':
            return s->snextc();
        default:
            return c;
        }
    }

    template <typename Handler>
    std::streambuf::int_type Scanner<Handler>::scanLexStringEOL2(std::streambuf::int_type c, std::streambuf *s)
    {
        switch ( c )
        {
        case '\n':
            return s->snextc();
        default:
            return c;
        }
    }

    template <typename Handler>
    std::streambuf::int_type Scanner<Handler>::scanDictionaryBeg(std::streambuf::int_type c, std::streambuf *s)
    {
        while ( c != std::streambuf::traits_type::eof() )
        {
            switch ( c )
            {
            case '\0':
            case '\t':
            case '\n':
            case '\f':
            case '\r':
            case ' ':
                break;
            case '<':
                static_cast<Handler*>(this)->onEnterDictionary();
                return s->snextc();
            default:
                static_cast<Handler*>(this)->onEnterString();
                c = scanHexString( c, s );
                static_cast<Handler*>(this)->onLeaveString();
                return c;
            }
            c = s->snextc();
        }
        return c;
    }

    template <typename Handler>
    std::streambuf::int_type Scanner<Handler>::scanDictionaryEnd(std::streambuf::int_type c, std::streambuf *s)
    {
        while ( c != std::streambuf::traits_type::eof() )
        {
            switch ( c )
            {
            case '\0':
            case '\t':
            case '\n':
            case '\f':
            case '\r':
            case ' ':
                break;
            case '>':
                static_cast<Handler*>(this)->onLeaveDictionary();
                return s->snextc();
            default:
                throw std::runtime_error("unexpected end of hexadecimal string");
            }
            c = s->snextc();
        }
        return c;
    }

    template <typename Handler>
    std::streambuf::int_type Scanner<Handler>::scanHexString(std::streambuf::int_type c, std::streambuf *s)
    {
        enum HexStringState { stOdd, stEven };

        std::streambuf::int_type hexChar = 0;
        HexStringState state = stOdd;

        while ( c != std::streambuf::traits_type::eof() )
        {
            switch (state)
            {
            case stOdd:
                switch (c)
                {
                case '\0':
                case '\t':
                case '\n':
                case '\f':
                case '\r':
                case ' ':
                    break;
                case '0':
                case '1':
                case '2':
                case '3':
                case '4':
                case '5':
                case '6':
                case '7':
                case '8':
                case '9':
                    hexChar = 16 * (c - '0');
                    state = stEven;
                    break;
                case '>':
                    return s->snextc();
                case 'a':
                case 'b':
                case 'c':
                case 'd':
                case 'e':
                case 'f':
                    hexChar = 16 * (10 + c - 'a');
                    state = stEven;
                    break;
                case 'A':
                case 'B':
                case 'C':
                case 'D':
                case 'E':
                case 'F':
                    hexChar = 16 * (10 + c - 'A');
                    state = stEven;
                    break;
                default:
                    throw std::runtime_error("invalid hexadecmal string character");
                }
                break;
            case stEven:
                switch (c)
                {
                case '\0':
                case '\t':
                case '\n':
                case '\f':
                case '\r':
                case ' ':
                    break;
                case '0':
                case '1':
                case '2':
                case '3':
                case '4':
                case '5':
                case '6':
                case '7':
                case '8':
                case '9':
                    static_cast<Handler*>(this)->onCharacter( static_cast<char>(hexChar + c - '0') );
                    state = stOdd;
                    break;
                case '>':
                    static_cast<Handler*>(this)->onCharacter( static_cast<char>(hexChar) );
                    return s->snextc();
                case 'a':
                case 'b':
                case 'c':
                case 'd':
                case 'e':
                case 'f':
                    static_cast<Handler*>(this)->onCharacter( static_cast<char>(hexChar + 10 + c - 'a') );
                    state = stOdd;
                    break;
                case 'A':
                case 'B':
                case 'C':
                case 'D':
                case 'E':
                case 'F':
                    static_cast<Handler*>(this)->onCharacter( static_cast<char>(hexChar + 10 + c - 'A') );
                    state = stOdd;
                    break;
                }
                break;
            }
            c = s->snextc();
        }
        return c;
    }

    template <typename Handler>
    std::streambuf::int_type Scanner<Handler>::scanToken(std::streambuf::int_type c, std::streambuf *s)
    {
        while ( c != std::streambuf::traits_type::eof() )
        {
            switch ( c )
            {
            case '\0':
            case '\t':
            case '\n':
            case '\f':
            case '\r':
            case ' ':
            case '%':
            case '(':
            case ')':
            case '/':
            case '<':
            case '>':
            case '[':
            case ']':
            case '{':
            case '}':
                return c;
            default:
                static_cast<Handler*>(this)->onCharacter( static_cast<char>( c ) );
            }
            c = s->snextc();
        }
        return c;
    }

    template <typename Handler>
    std::streambuf::int_type Scanner<Handler>::scanName(std::streambuf::int_type c, std::streambuf *s)
    {
        while ( c != std::streambuf::traits_type::eof() )
        {
            switch ( c )
            {
            case '\0':
            case '\t':
            case '\n':
            case '\f':
            case '\r':
            case ' ':
            case '%':
            case '(':
            case ')':
            case '/':
            case '<':
            case '>':
            case '[':
            case ']':
            case '{':
            case '}':
                return c;
            case '#':
                c = scanNameEsc( s->snextc(), s );
                break;
            default:
                static_cast<Handler*>(this)->onCharacter( static_cast<char>( c ) );
                c = s->snextc();
            }
        }
        return c;
    }

    template <typename Handler>
    std::streambuf::int_type Scanner<Handler>::scanNameEsc(std::streambuf::int_type c, std::streambuf *s)
    {
        std::streambuf::int_type hexChar = 0;

        if ( c != std::streambuf::traits_type::eof())
        {
            switch (c)
            {
            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
                hexChar = 16 * (c - '0');
                break;
            case 'a':
            case 'b':
            case 'c':
            case 'd':
            case 'e':
            case 'f':
                hexChar = 16 * (10 + c - 'a');
                break;
            case 'A':
            case 'B':
            case 'C':
            case 'D':
            case 'E':
            case 'F':
                hexChar = 16 * (10 + c - 'A');
                break;
            default:
                throw std::runtime_error("invalid hexadecmal character");
            }
            c = s->snextc();
        }

        if ( c != std::streambuf::traits_type::eof())
        {
            switch (c)
            {
            case '0':
            case '1':
            case '2':
            case '3':
            case '4':
            case '5':
            case '6':
            case '7':
            case '8':
            case '9':
                static_cast<Handler*>(this)->onCharacter( static_cast<char>( hexChar + c - '0' ) );
                break;
            case 'a':
            case 'b':
            case 'c':
            case 'd':
            case 'e':
            case 'f':
                static_cast<Handler*>(this)->onCharacter( static_cast<char>( hexChar + 10 + c - 'a' ) );
                break;
            case 'A':
            case 'B':
            case 'C':
            case 'D':
            case 'E':
            case 'F':
                static_cast<Handler*>(this)->onCharacter( static_cast<char>( hexChar + 10 + c - 'A' ) );
                break;
            default:
                throw std::runtime_error("invalid hexadecmal character");
            }
            c = s->snextc();
        }

        return c;
    }
}